/*
 * Copyright [2022] [https://www.xiaonuo.vip]
 *
 * Snowy采用APACHE LICENSE 2.0开源协议，您在使用过程中，需要注意以下几点：
 *
 * 1.请不要删除和修改根目录下的LICENSE文件。
 * 2.请不要删除和修改Snowy源码头部的版权声明。
 * 3.本项目代码可免费商业使用，商业使用请保留源码和相关描述文件的项目出处，作者声明等。
 * 4.分发源码时候，请注明软件出处 https://www.xiaonuo.vip
 * 5.不可二次分发开源参与同类竞品，如有想法可联系团队xiaonuobase@qq.com商议合作。
 * 6.若您的项目无法满足以上几点，需要更多功能代码，获取Snowy商业授权许可，请在官网购买授权，地址为 https://www.xiaonuo.vip
 */
package vip.xiaonuo.biz.modular.cabinetdoor.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.NumberUtils;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.core.util.StpClientUtil;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.biz.core.enums.BizDeviceTypeEnum;
import vip.xiaonuo.biz.modular.cabinetdoor.entity.BizCabinetDoor;
import vip.xiaonuo.biz.modular.cabinetdoor.mapper.BizCabinetDoorMapper;
import vip.xiaonuo.biz.modular.cabinetdoor.param.*;
import vip.xiaonuo.biz.modular.cabinetdoor.service.BizCabinetDoorService;
import vip.xiaonuo.biz.modular.cabinetdoor.vo.*;
import vip.xiaonuo.biz.modular.cabinetgroup.entity.BizCabinetGroup;
import vip.xiaonuo.biz.modular.cabinetgroup.service.BizCabinetGroupService;
import vip.xiaonuo.biz.modular.cabinetinfo.entity.BizCabinetInfo;
import vip.xiaonuo.biz.modular.cabinetinfo.service.BizCabinetInfoService;
import vip.xiaonuo.biz.modular.cabinetpermission.service.BizCabinetPermissionService;
import vip.xiaonuo.biz.modular.cabinetpermission.service.BizCabinetPermissionUtil;
import vip.xiaonuo.biz.modular.goods.mapper.BizGoodsMapper;
import vip.xiaonuo.biz.modular.goods.result.GoodsQuantityResult;
import vip.xiaonuo.biz.modular.relation.entity.BizRelation;
import vip.xiaonuo.biz.modular.relation.enums.BizRelationCategoryEnum;
import vip.xiaonuo.biz.modular.relation.service.BizRelationService;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.dev.api.DevDictApi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 柜门信息Service接口实现类
 *
 * @author kongxm
 * @date 2022/11/13 16:14
 **/
@Service
public class BizCabinetDoorServiceImpl extends ServiceImpl<BizCabinetDoorMapper, BizCabinetDoor> implements BizCabinetDoorService {
    @Autowired
    private BizRelationService bizRelationService;
    @Autowired
    private BizCabinetGroupService bizCabinetGroupService;
    @Autowired
    private BizCabinetInfoService bizCabinetInfoService;
    @Autowired
    private BizCabinetPermissionService bizCabinetPermissionService;
    @Autowired
    private BizGoodsMapper bizGoodsMapper;
    @Autowired
    private BizCabinetPermissionUtil bizCabinetPermissionUtil;
    @Autowired
    private DevDictApi devDictApi;

    @Override
    public Page<BizCabinetDoorResp> page(BizCabinetDoorPageParam param) {
        if (!StpUtil.hasRoleOr("superAdmin", "sysAdmin")) {
            SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
            param.setUserId(saBaseLoginUser.getId());
        }
        return getBaseMapper().page(Page.of(param.getCurrent(), param.getSize()), param);
    }

    @Override
    public void add(BizCabinetDoorAddParam bizCabinetDoorAddParam) {
        BizCabinetDoor bizCabinetDoor = BeanUtil.toBean(bizCabinetDoorAddParam, BizCabinetDoor.class);
        this.save(bizCabinetDoor);
    }

    @Override
    public void edit(BizCabinetDoorEditParam param) {
        bizCabinetPermissionUtil.hasPermission(null, null, param.getId());
        BizCabinetDoor bizCabinetDoor = this.queryEntity(param.getId());
        BeanUtil.copyProperties(param, bizCabinetDoor);

        // if the configuration isn't separated, follow the cabinet info
        if (!param.getMakingInventoryConfigSeparated()) {
            BizCabinetInfo cabinetInfo = bizCabinetInfoService.getById(bizCabinetDoor.getCabinetInfoId());
            if (cabinetInfo != null) {
                bizCabinetDoor.setMakingInventoryUnit(cabinetInfo.getMakingInventoryUnit());
                bizCabinetDoor.setMakingInventoryCycle(cabinetInfo.getMakingInventoryCycle());
            }
        }

        this.updateById(bizCabinetDoor);
        if (StringUtils.isNotEmpty(param.getAdminUserIds())) {
            bizRelationService.saveRelationBatchWithClear(bizCabinetDoor.getId(),
                    Arrays.stream(param.getAdminUserIds().split(",")).collect(Collectors.toList()),
                    BizRelationCategoryEnum.BIZ_RELATION_CABINET_DOOR_AND_ADMIN_USER.getValue());
        } else {
            bizRelationService.remove(new LambdaQueryWrapper<BizRelation>()
                    .eq(BizRelation::getObjectId, bizCabinetDoor.getId()));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<BizCabinetDoorIdParam> bizCabinetDoorIdParamList) {
        bizCabinetDoorIdParamList.forEach(item -> bizCabinetPermissionUtil.hasPermission(null, null, item.getId()));
        // 执行删除
        this.removeBatchByIds(CollStreamUtil.toList(bizCabinetDoorIdParamList, BizCabinetDoorIdParam::getId));
    }

    @Override
    public BizCabinetDoor detail(BizCabinetDoorIdParam bizCabinetDoorIdParam) {
        return this.queryEntity(bizCabinetDoorIdParam.getId());
    }

    @Override
    public BizCabinetDoor queryEntity(String id) {
        BizCabinetDoor bizCabinetDoor = this.getById(id);
        if (ObjectUtil.isEmpty(bizCabinetDoor)) {
            throw new CommonException("柜门信息不存在，id值为：{}", id);
        }
        return bizCabinetDoor;
    }

    @Override
    public List<BizCabinetDoor> getList(BizCabinetDoorIdParam bizCabinetDoorIdParam) {
        return this.baseMapper.getListByCabId(bizCabinetDoorIdParam);
    }

    @Override
    public boolean switchState(String id) {
        Assert.notNull(id, "id不能为空");
        BizCabinetDoor bizCabinetDoor = getById(id);
        Assert.notNull(bizCabinetDoor, "记录不存在");
        BizCabinetDoor updateBean = new BizCabinetDoor();
        updateBean.setId(id);
        updateBean.setState(bizCabinetDoor.getState() == 1 ? 0 : 1);
        return this.updateById(updateBean);
    }

    @Override
    public ClientCabinetDoorDetail getDetailForClient(String cabinetDoorId) {
        String userId = StpClientUtil.getLoginIdAsString();
        ClientCabinetDoorDetail cabinetDoorDetail = getBaseMapper().getDetailForClient(cabinetDoorId);
        Assert.notNull(cabinetDoorDetail, "柜门不存在");
        if (cabinetDoorDetail.getCabinetDoorType() == 1) { // 试剂
            // 查询试剂存放类型
            String reagentStorageTypeName = devDictApi.getLabelByCode(cabinetDoorDetail.getReagentStorageType());
            cabinetDoorDetail.setReagentStorageTypeName(reagentStorageTypeName);
            // 查询一共存放了多少克
            double totalWeigh = bizGoodsMapper.sumReagentWeighByCabinetDoorId(cabinetDoorDetail.getCabinetDoorId());
            if (totalWeigh > 1000) {
                cabinetDoorDetail.setReagentTotalWeigh(NumberUtil.decimalFormat("#.##", (totalWeigh / 1000)) + " kg");
            } else {
                cabinetDoorDetail.setReagentTotalWeigh(NumberUtil.decimalFormat("#.##",totalWeigh) + " g");
            }
        }
        /**
         * 待授权	UNAUTHORIZED
         * 已拒绝	REFUSED
         * 已授权	AUTHORIZED
         * 已冻结	FROZEN
         * 已过期	EXPIRED
         */
        boolean hasPerm = bizCabinetPermissionService.hasDoorPermWithCurrentUser(cabinetDoorDetail.getSeparatedPermission(),
                cabinetDoorDetail.getCabinetDoorId(),
                cabinetDoorDetail.getCabinetId(),
                cabinetDoorDetail.getCabinetGroupId(),
                userId);
        cabinetDoorDetail.setHasPerm(hasPerm);
        return cabinetDoorDetail;
    }

    @Override
    public List<ClientCabinetDoorResp> listCabinetDoorForClientUserPerm(ClientCabinetDoorIdParam param) {
        return this.getBaseMapper().listCabinetDoorForClient(param).stream()
                .map(item -> {
                    ClientCabinetDoorResp resp = new ClientCabinetDoorResp();
                    resp.setId(item.getId());
                    resp.setCabinetDoorName(item.getCabinetDoorName());
                    resp.setCabinetDoorCode(item.getCabinetDoorCode());
                    Integer maxPermDays = item.getMaxPermDays();
                    if (maxPermDays == null) maxPermDays = item.getMaxPermDaysCabinetInfo();
                    if (maxPermDays == null) maxPermDays = item.getMaxPermDaysCabinetGroup();
                    resp.setMaxPermDays(maxPermDays);
                    return resp;
                }).collect(Collectors.toList());
    }

    @Override
    public List<ClientCabinetTree> getCabinetTree(String searchKey, Integer goodsType) {
        String userId = StpClientUtil.getLoginIdAsString();

        // 找出当前人所拥有的所有组柜
        List<BizCabinetGroup> cabinetGroupList = bizCabinetGroupService.listCabinetGroupByAdminId(userId, goodsType);
        // 找出当前人所拥有的所有柜子
        List<BizCabinetInfo> cabinetInfoList = bizCabinetInfoService.listCabinetInfoByAdminId(userId, searchKey, goodsType, null);
        // 找出当前人所拥有的所有柜门
        List<BizCabinetDoor> cabinetDoorList = getBaseMapper().listCabinetDoorByAdminId(userId, goodsType);
        // 找出当前人所拥有的柜门的物品存放情况
        List<GoodsQuantityResult> goodsInCabinetList = bizGoodsMapper.queryInCabinetGoodsQuantity(userId, goodsType);
        List<GoodsQuantityResult> goodsOutCabinetList = bizGoodsMapper.queryOutCabinetGoodsQuantity(userId, goodsType);
        // 构建树
        List<ClientCabinetTree> treeList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(cabinetGroupList)) {
            cabinetGroupList.forEach(cabinetGroup -> {
                ClientCabinetTree cabinetGroupTree = getClientCabinetTree(cabinetGroup);
                treeList.add(cabinetGroupTree);
                //将组柜ID不为空 的 并且有组柜的柜子, 挂在组柜上
                cabinetInfoList.stream().filter(cabinetInfo ->
                        StringUtils.isNotEmpty(cabinetInfo.getCabinetGroupId()) && cabinetGroup.getId().equals(cabinetInfo.getCabinetGroupId()))
                        .forEach(cabinetInfo -> {
                            ClientCabinetTree cabinetInfoTree = getClientCabinetTree(cabinetInfo);
                            cabinetGroupTree.addChild(cabinetInfoTree);
                            // 将柜门挂在柜子上
                            cabinetDoorList.stream().filter(cabinetDoor -> cabinetInfo.getId().equals(cabinetDoor.getCabinetInfoId()))
                                    .forEach(cabinetDoor ->
                                            cabinetInfoTree.addChild(getClientCabinetTree(cabinetDoor, goodsInCabinetList, goodsOutCabinetList))
                                    );
                        });
            });
        }
        // 将组柜id为空的，或者没有组柜的柜子，挂在根结点上
        cabinetInfoList.stream().filter(cabinetInfo ->
                StringUtils.isEmpty(cabinetInfo.getCabinetGroupId()) // 组柜id为空
                        || cabinetGroupList.stream().noneMatch(cabinetGroup -> cabinetGroup.getId().equals(cabinetInfo.getCabinetGroupId()))) // 没有对应组柜的柜子
                .forEach(cabinetInfo -> {
                    ClientCabinetTree cabinetInfoTree = getClientCabinetTree(cabinetInfo);
                    treeList.add(cabinetInfoTree);
                    // 将柜门挂在柜子上
                    cabinetDoorList.stream().filter(cabinetDoor -> cabinetInfo.getId().equals(cabinetDoor.getCabinetInfoId()))
                            .forEach(cabinetDoor ->
                                    cabinetInfoTree.addChild(getClientCabinetTree(cabinetDoor, goodsInCabinetList, goodsOutCabinetList))
                            );
                });
        return treeList;
    }

    /**
     * 组装组柜树结点
     *
     * @param cabinetGroup
     * @return
     */
    private ClientCabinetTree getClientCabinetTree(BizCabinetGroup cabinetGroup) {
        return ClientCabinetTree.builder()
                .id(cabinetGroup.getId())
                .name(cabinetGroup.getCabinetGroupName())
                .deviceType(BizDeviceTypeEnum.CABINET_GROUP.name())
                .build();
    }

    /**
     * 组装柜子树结点
     *
     * @param cabinetInfo
     * @return
     */
    private ClientCabinetTree getClientCabinetTree(BizCabinetInfo cabinetInfo) {
        return ClientCabinetTree.builder()
                .id(cabinetInfo.getId())
                .name(cabinetInfo.getCabinetName())
                .deviceType(BizDeviceTypeEnum.CABINET_INFO.name())
                .build();
    }

    /**
     * 组装柜门树结点
     *
     * @param cabinetDoor
     * @return
     */
    private ClientCabinetTree getClientCabinetTree(BizCabinetDoor cabinetDoor,
                                                   List<GoodsQuantityResult> goodsInCabinetList,
                                                   List<GoodsQuantityResult> goodsOutCabinetList) {
        ClientCabinetTree clientCabinetTree = ClientCabinetTree.builder()
                .id(cabinetDoor.getId())
                .name(cabinetDoor.getCabinetDoorName())
                .deviceType(BizDeviceTypeEnum.CABINET_DOOR.name())
                .goodsInCabinetQuantity(0L)
                .goodsOutOfCabinetQuantity(0L)
                .build();
        goodsInCabinetList.stream().filter(item -> item.getCabinetDoorId().equals(cabinetDoor.getId()))
                .findAny().ifPresent(item -> clientCabinetTree.setGoodsInCabinetQuantity(item.getQuantity()));

        goodsOutCabinetList.stream().filter(item -> item.getCabinetDoorId().equals(cabinetDoor.getId()))
                .findAny().ifPresent(item -> clientCabinetTree.setGoodsOutOfCabinetQuantity(item.getQuantity()));

        return clientCabinetTree;
    }

    @Override
    public List<ClientCabinetDoorResp> listCabinetDoorForClientUserEvent(ClientCabinetDoorIdParam param) {
        String userId = StpClientUtil.getLoginIdAsString();
        param.setUserId(userId);
        return getBaseMapper().listCabinetDoorForClientUserEvent(param);
    }

    @Override
    public List<ClientCabinetDoor> listCabinetDoorByGoodsForClient() {
        return getBaseMapper().listCabinetDoorByGoodsForClient();
    }

    @Override
    public List<ClientCabinetDoorForAdmin> listCabinetDoorByAdminId(String userId, Integer cabinetDoorType, String cabinetInfoId) {
        return getBaseMapper().listCabinetDoorByAdminIdForClient(userId, cabinetDoorType, cabinetInfoId);
    }

    public static void main(String[] args) {
        System.out.println(NumberUtil.decimalFormat("#.##",1234.11111));
    }
}
